using System.Net.Http;
using System.Text;
using System.Text.Json;
using DocumentCreationSystem.Models;
using Microsoft.Extensions.Logging;

namespace DocumentCreationSystem.Services
{
    /// <summary>
    /// ai00-mem 适配器，通过其HTTP API进行读写
    /// 端点规范（自洽方案）：
    /// - 健康检查：GET /health → 200 且 { status:"ok" }
    /// - 写入：POST /memories → { id }
    /// - 检索：POST /query → { results:[{id,relevance,content,attributes}] }
    /// </summary>
    public class Ai00MemAdapter : IMemoryService
    {
        private readonly ILogger<Ai00MemAdapter> _logger;
        private readonly HttpClient _http;
        private string _baseUrl = "http://localhost:7007";
        private float _threshold = 0.7f;
        private bool _healthy = false;
        private int _failureCount = 0;
        private DateTime _nextRetryAt = DateTime.MinValue;

        public Ai00MemAdapter(ILogger<Ai00MemAdapter> logger, HttpClient http)
        {
            _logger = logger; _http = http;
            _http.Timeout = TimeSpan.FromSeconds(15);
        }

        public async Task<bool> InitializeAsync(MemoryConfig config)
        {
            _baseUrl = string.IsNullOrWhiteSpace(config.Ai00MemBaseUrl) ? _baseUrl : config.Ai00MemBaseUrl.Trim();
            _threshold = config.SimilarityThreshold;

            _healthy = await HealthCheckAsync();
            if (!_healthy)
            {
                _logger.LogWarning("Ai00Mem 健康检查失败: {BaseUrl}", _baseUrl);
            }
            else
            {
                _logger.LogInformation("Ai00Mem 健康检查通过: {BaseUrl}", _baseUrl);
            }
            return _healthy;
        }

        public async Task<string> AddMemoryAsync(string content, string? userId = null, string? sessionId = null, Dictionary<string, object>? attributes = null)
        {
            EnsureHealthyOrThrow();
            var body = new { content, user_id = userId, session_id = sessionId, attributes = attributes ?? new Dictionary<string, object>() };
            var json = JsonSerializer.Serialize(body);
            var resp = await _http.PostAsync(Combine("/memories"), new StringContent(json, Encoding.UTF8, "application/json"));
            resp.EnsureSuccessStatusCode();
            var text = await resp.Content.ReadAsStringAsync();
            using var doc = JsonDocument.Parse(text);
            var id = doc.RootElement.TryGetProperty("id", out var idEl) ? idEl.GetString() : Guid.NewGuid().ToString();
            return id ?? Guid.NewGuid().ToString();
        }

        public async Task<List<MemoryResult>> QueryAsync(string query, string? userId = null, int topK = 5, float threshold = 0.7f)
        {
            EnsureHealthyOrThrow();
            var k = topK > 0 ? topK : 5;
            var th = threshold > 0 ? threshold : _threshold;
            var body = new { text = query, user_id = userId, limit = k, min_relevance = th };
            var json = JsonSerializer.Serialize(body);
            var resp = await _http.PostAsync(Combine("/query"), new StringContent(json, Encoding.UTF8, "application/json"));
            resp.EnsureSuccessStatusCode();
            var text = await resp.Content.ReadAsStringAsync();
            using var doc = JsonDocument.Parse(text);
            var list = new List<MemoryResult>();
            if (doc.RootElement.TryGetProperty("results", out var results) && results.ValueKind == JsonValueKind.Array)
            {
                foreach (var r in results.EnumerateArray())
                {
                    list.Add(new MemoryResult
                    {
                        Id = r.TryGetProperty("id", out var idEl) ? idEl.GetString() ?? "" : "",
                        Score = r.TryGetProperty("relevance", out var scEl) ? scEl.GetSingle() : 0,
                        Content = r.TryGetProperty("content", out var ctEl) ? ctEl.GetString() ?? "" : "",
                        Attributes = r.TryGetProperty("attributes", out var atEl) && atEl.ValueKind == JsonValueKind.Object
                            ? JsonSerializer.Deserialize<Dictionary<string, object>>(atEl.GetRawText()) ?? new()
                            : new()
                    });
                }
            }
            return list;
        }

        private async Task<bool> HealthCheckAsync()
        {
            // 指数退避 + 半开路：异常时逐步延迟下次探测；到期则再次探测
            try
            {
                if (DateTime.UtcNow < _nextRetryAt && !_healthy)
                {
                    return false;
                }

                var resp = await _http.GetAsync(Combine("/health"));
                if (!resp.IsSuccessStatusCode)
                {
                    RegisterFailure();
                    return false;
                }

                var text = await resp.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(text);
                if (doc.RootElement.TryGetProperty("status", out var s))
                {
                    var ok = string.Equals(s.GetString(), "ok", StringComparison.OrdinalIgnoreCase);
                    if (ok) RegisterSuccess(); else RegisterFailure();
                    return ok;
                }
                // 只要200也认为OK
                RegisterSuccess();
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Ai00Mem 健康检查异常");
                RegisterFailure();
                return false;
            }
        }

        private void EnsureHealthyOrThrow()
        {
            if (!_healthy)
            {
                // 触发一次半开路健康检查（非阻塞，可按需更改为同步）
                _ = HealthCheckAsync();
                throw new InvalidOperationException("Ai00Mem 服务不可用");
            }
        }

        private void RegisterFailure()
        {
            _healthy = false;
            _failureCount = Math.Min(_failureCount + 1, 6);
            // 退避：1s, 2s, 4s, 8s, 16s, 32s（上限）
            var delaySecs = (int)Math.Pow(2, Math.Max(0, _failureCount - 1));
            _nextRetryAt = DateTime.UtcNow.AddSeconds(Math.Clamp(delaySecs, 1, 32));
        }

        private void RegisterSuccess()
        {
            _healthy = true;
            _failureCount = 0;
            _nextRetryAt = DateTime.UtcNow;
        }

        private string Combine(string path)
        {
            var baseUrl = _baseUrl.EndsWith("/") ? _baseUrl.TrimEnd('/') : _baseUrl;
            var p = path.StartsWith("/") ? path : "/" + path;
            return baseUrl + p;
        }
    }
}

